package com.aner.test.config;

import com.aner.test.security.filter.JwtAuthenticationTokenFilter;
import com.aner.test.security.handle.AuthenticationEntryPointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * spring security configuration
 *
 * @author aner
 */
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
  /**
   * custom authentication service
   */
  @Autowired
  private UserDetailsService userDetailsService;

  /**
   * authentication failure handler
   */
  @Autowired
  private AuthenticationEntryPointImpl unauthorizedHandler;

  /**
   * token authentication filter
   */
  @Autowired
  private JwtAuthenticationTokenFilter authenticationTokenFilter;

  /**
   *
   *
   * @return
   * @throws Exception
   */
  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception
  {
    return super.authenticationManagerBean();
  }

  /**
   * anyRequest          |   match all request
   * access              |   enable when the result of SpringEl is true
   * anonymous           |   anonymous access
   * permitAll           |   permit all user access
   * rememberMe          |   permit user to access by remember-me method
   * authenticated       |   user has logged in successfully
   */
  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception
  {
    httpSecurity
            .csrf().disable()
            // authentication failure handler
            .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
            // set session stateless, because of being based on token authentication
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            // filter request
            .authorizeRequests()
            // permit login api anonymous access
            .antMatchers("/login").anonymous()
            .antMatchers(
                    HttpMethod.GET,
                    "/*.html",
                    "/**/*.html",
                    "/**/*.css",
                    "/**/*.js"
            ).permitAll()
            .antMatchers("/swagger-ui.html").anonymous()
            .antMatchers("/swagger-resources/**").anonymous()
            .antMatchers("/webjars/**").anonymous()
            .antMatchers("/*/api-docs").anonymous()
            .antMatchers("/druid/**").anonymous()
            .anyRequest().authenticated()
            .and()
            .headers().frameOptions().disable();
    // add JWT filter
    httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
  }


  /**
   * password crypt encoder
   *
   * @return
   */
  @Bean
  public BCryptPasswordEncoder bCryptPasswordEncoder()
  {
    return new BCryptPasswordEncoder();
  }

  /**
   * user identification method
   *
   * @param auth
   * @throws Exception
   */
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception
  {
    auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
  }
}
